/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2021-2022. All rights reserved.
 *
 */

import process from 'process';

import { CUR_HVIGOR_VERSION } from '../../cli/wrapper/wrapper-const.js';
import { HookType } from '../../common/const/hook-const.js';
import { processLegacyConfig } from '../daemon/util/process-build-analyze-mode.js';
import { Project } from '../external/core/hvigor-core-node.js';
import { hvigor } from '../external/core/hvigor.js';
import { HvigorTaskGroupType } from '../external/task/core-task.js';
import { globalData } from '../internal/data/global-data.js';
import { buildTaskGraph } from '../internal/lifecycle/build-task-graph.js';
import { configuration } from '../internal/lifecycle/configuration.js';
import { ExecuteModeFactory, TaskExecuteOptions } from '../internal/lifecycle/execute/mode/execute-mode-factory.js';
import { HvigorLifecycleHook } from '../internal/lifecycle/hook/hvigor-lifecycle-hook.js';
import { hvigorProcess } from '../internal/lifecycle/hvigor-process.js';
import { init } from '../internal/lifecycle/init.js';
import { DurationEvent } from '../metrics/event/duration-event.js';
import { MetricLogType } from '../metrics/event/log-event.js';
import { MetricFactory } from '../metrics/metric-factory.js';
import { HvigorCliOptions } from '../util/options/hvigor-cli-options.js';
import { initEnvConfigProps } from './hooks/init-env-config-props.js';
import { addExtensionHandler } from './hooks/require-hook.js';

const modeAlias = 'mode';

export async function boot(opts: HvigorCliOptions) {
  try {
    addExtensionHandler(['.ts', '.mjs']);

    const env = {
      cwd: process.cwd(),
      configProps: null,
      version: CUR_HVIGOR_VERSION,
    };
    globalData.init(env, opts);
    hvigorProcess.init();
    initEnvConfigProps();
    processLegacyConfig(opts);

    hvigorProcess.setTaskBeginTime();
    hvigorProcess.startProcessMonitor();
    await start();
  } catch (e: unknown) {
    if (e instanceof Error) {
      hvigorProcess.error(e);
    }
  }
}

async function start(): Promise<void> {
  const initTaskName = 'init';
  const initEventName = 'create hvigor project model';
  const configurationEventName = 'configure hvigor plugin';
  const buildTaskGraphEventName = 'build task graph';
  const taskExecuteOptionEventName = 'init task execution option';
  const durationEvent: DurationEvent = MetricFactory.createDurationEvent(initTaskName, 'Initialize and build task graph.', HvigorTaskGroupType.INIT_TASK_GROUP);
  const initEvent = durationEvent.createSubEvent(initEventName, 'Initialize hvigor project model.');
  const configurationEvent = durationEvent.createSubEvent(configurationEventName, 'Configure hvigor plugin.');
  const buildTaskGraphEvent = durationEvent.createSubEvent(buildTaskGraphEventName, 'Build task graph.');
  const taskExecuteOptionEvent = durationEvent.createSubEvent(taskExecuteOptionEventName, 'Init task execution option.');
  durationEvent.start();
  initEvent.start();
  const opts = globalData.cliOpts;
  const env = globalData.cliEnv;

  /*
   * 根据初始化的环境变量,来创建hvigor的构建工程模型
   * 1.将合并后的命令行的参数和数据保存到单例的VigorConfig对象中
   * 2.根据工程的build-profile.json初始化project和module对象模型
   */
  await init(durationEvent);
  initEvent.stop().setLog(initEventName, MetricLogType.INFO);

  configurationEvent.start();

  // 根据工程和每个module的hvigorfile文件的进行Plugin的初始化和配置
  const project: Project = (await configuration(configurationEvent)) as Project;
  configurationEvent.stop().setLog(configurationEventName, MetricLogType.INFO);

  buildTaskGraphEvent.start();

  // 初始化每个Node的DAG图
  buildTaskGraph(project);
  await HvigorLifecycleHook.getInstance().runHook(HookType.taskGraphResolved, hvigor, durationEvent);
  buildTaskGraphEvent.stop().setLog(buildTaskGraphEventName, MetricLogType.INFO);

  taskExecuteOptionEvent.start();

  // 初始化task执行配置
  const options: TaskExecuteOptions = {
    toRunTasks: opts._,
    isNeedSync: !!opts.sync,
  };
  taskExecuteOptionEvent.stop().setLog(taskExecuteOptionEventName, MetricLogType.INFO);

  durationEvent.stop().setLog(initTaskName, MetricLogType.INFO);

  // 根据不同的Mode创建管道,来执行对应的任务
  const executeMode = env.configProps.get(modeAlias);
  await new ExecuteModeFactory(project, options).getExecutePipeline(executeMode).startPipeline();
}
